package webjoch.wrappers;

import org.rsbuddy.tabs.Inventory;

import webjoch.Main;
import webjoch.methods.Rooms;
import webjoch.misc.Constants;
import webjoch.misc.Functions;
import webjoch.process.strategies.SolvedPuzzle;

import com.rsbuddy.script.methods.Calculations;
import com.rsbuddy.script.methods.Camera;
import com.rsbuddy.script.methods.Game;
import com.rsbuddy.script.methods.Objects;
import com.rsbuddy.script.methods.Players;
import com.rsbuddy.script.task.Task;
import com.rsbuddy.script.util.Filter;
import com.rsbuddy.script.wrappers.GameObject;
import com.rsbuddy.script.wrappers.Tile;

/**
 * A door in a room
 * @author Webjoch
 *
 */
public class Door{
	private GameObject door;
	private Tile location;
	private Room room;
	
	public enum Type{
		NORMAL, GUARDIAN, KEY, SKILL
	}
	
	public enum Rotation {
		WEST(0),
		NORTH(1),
		EAST(2),
		SOUTH(3);
		int rotation;
		
		Rotation(int rotation){
			this.rotation = rotation;
		}
		
		public int getRoomRotation(){
			return rotation;
		}
	}
	
	public Door(GameObject door){
		this.door = door;
		this.location = door.getLocation();
		this.room = Rooms.getRoom(door.getLocation());
	}
	
	public Door(GameObject door, Room room){
		this.door = door;
		this.location = door.getLocation();
		this.room = room;
	}
	
	/**
	 * Gets the door type
	 * @return returns normal, guardian, key or skill door
	 */
	public Type getType(){
		if (Functions.inArray(Constants.GUARDIAN_DOOR, door.getId())) return Type.GUARDIAN;
		if (Functions.inArray(Constants.KEY_DOORS, door.getId() - 145)) return Type.KEY;
		if (getSkillObject() != null) return Type.SKILL;
		return Type.NORMAL;
	}
	
	/**
	 * Gets the door object
	 * @return door object
	 */
	public GameObject getObject(){
		return door;
	}
	
	/**
	 * Gets the door object id
	 * @return door object id
	 */
	public int getId(){
		return door.getId();
	}
	
	/**
	 * Gets the doors location
	 * @return door location
	 */
	public Tile getLocation(){
		return location;
	}
	
	/**
	 * Gets the door rotation
	 * @return returns north, east, south or west
	 */
	public Rotation getRotation(){
		Tile tile1 = room.getArea().getTileArray()[room.getArea().getTileArray().length-1];
		Tile tile2 = room.getArea().getTileArray()[0];
		
		if (door.getLocation().getY() == tile1.getY()) return Rotation.NORTH;
		if (door.getLocation().getX() == tile1.getX()) return Rotation.EAST;
		if (door.getLocation().getY() == tile2.getY()) return Rotation.SOUTH;
		if (door.getLocation().getX() == tile2.getX()) return Rotation.WEST;
		return null;
	}
	
	public Tile getRoomTileBehindDoor(){
		if (getRotation() == Rotation.NORTH) return new Tile(door.getLocation().getX(), door.getLocation().getY() + 5);
		if (getRotation() == Rotation.EAST) return new Tile(door.getLocation().getX() + 5, door.getLocation().getY());
		if (getRotation() == Rotation.SOUTH) return new Tile(door.getLocation().getX(), door.getLocation().getY() - 5);
		if (getRotation() == Rotation.WEST) return new Tile(door.getLocation().getX() - 5, door.getLocation().getY());
		return null;
	}
	
	public Tile getTileBeforeDoor(){
		if (getRotation() == Rotation.NORTH) return new Tile(door.getLocation().getX(), door.getLocation().getY() - 2);
		if (getRotation() == Rotation.EAST) return new Tile(door.getLocation().getX() - 2, door.getLocation().getY());
		if (getRotation() == Rotation.SOUTH) return new Tile(door.getLocation().getX(), door.getLocation().getY() + 2);
		if (getRotation() == Rotation.WEST) return new Tile(door.getLocation().getX() + 2, door.getLocation().getY());
		return null;
	}
	
	/**
	 * Checks if the door is opened
	 * @return returns true if the door is open
	 */
	public boolean isOpen(){
		try{
			return Rooms.getRoom(getRoomTileBehindDoor()).isValid();
		}catch(NullPointerException e){
			return false;
		}
	}
	
	/**
	 * Get the lock on the door
	 * @return the lock object or null if there isn't one
	 */
	public GameObject getLock(){
		GameObject Lock = Objects.getNearest(new Filter<GameObject>(){
			public boolean accept(GameObject o){
				return Functions.inArray(Constants.KEY_DOORS, o.getId()) && Calculations.distanceBetween(door.getLocation(), o.getLocation()) < 5;
			}
		});
		if (Lock == null) return null;
		return Calculations.distanceBetween(door.getLocation(), Lock.getLocation()) < 5 ? Lock : null;
	}
	
	/**
	 * Checks if the door is locked
	 * @return returns true if the door is locked
	 */
	public boolean isLocked(){
		if (getType() != Type.KEY) return false;
		return getLock() != null;
	}
	
	/**
	 * Gets the needed key id from for the lock
	 * @return needed key id
	 */
	public int getKeyId(){
		return 18202 + (door.getId() - 50353) * 2;
	}
	
	/**
	 * Add the link to the adjacent room
	 */
	public void addLink(){
		Room newRoom = Rooms.getRoom(getRoomTileBehindDoor());
		if (newRoom.isValid()){
			Rotation thisRotation = getRotation();
			Rotation otherRotation = null;
			
			if (thisRotation == Rotation.NORTH) otherRotation = Rotation.SOUTH;
			if (thisRotation == Rotation.WEST) otherRotation = Rotation.EAST;
			if (thisRotation == Rotation.SOUTH) otherRotation = Rotation.NORTH;
			if (thisRotation == Rotation.EAST) otherRotation = Rotation.WEST;
			
			Link link1 = new Link(room, thisRotation, newRoom);
			Link link2 = new Link(newRoom, otherRotation, room);
			
			Main.getDungeon().addRoom(link1);
			Main.getDungeon().addRoom(link2);
		}
	}

	/**
	 * Get the skill object on the door
	 * @return returns the skill object or null if there isn't one
	 */
	public GameObject getSkillObject(){
		if (Functions.inArray(Constants.SKILL_DOORS, door.getId())) return door;
		GameObject[] skillObject = Objects.getLoaded(new Filter<GameObject>(){
			public boolean accept(GameObject o){
				return Functions.inArray(Constants.SKILL_DOORS, o.getId()) && Calculations.distanceBetween(getLocation(), o.getLocation()) < 3;
			}
		});
		if (skillObject.length != 1) return null;
		return skillObject[0];
	}
	
	/**
	 * Gets the needed item id to destroy the skill object
	 * @return returns one of the needed item id`s
	 */
	public int[] getNeededSkillItem(){
		GameObject skillObject =  getSkillObject();
		if (Functions.inArray(Constants.SKILL_DOORS_NOITEM, skillObject.getId())) return new int[] {0};
		if (Functions.inArray(Constants.FLAMMABLE_DEBRIS , skillObject.getId())) return Constants.TINERBOX;
		if (Functions.inArray(Constants.WOODEN_BARRICADE , skillObject.getId())) return Constants.HATCHET;
		if (Functions.inArray(Constants.PILE_OF_ROCKS, skillObject.getId())) return Constants.PICKAXE;
		if (Functions.inArray(Constants.BROKEN_KEY_DOOR, skillObject.getId())) return Constants.HAMMER;
		return new int[] {0};
	}
	
	/**
	 * Checks if it is possible to open the door
	 * @return returns true if you can open the door
	 */
	public boolean canOpen(){
		if (isOpen()) return false;
		if (Main.getDungeon().shouldIgnoreDoor(this)) return false;
		if (room.getType() == Room.Type.PUZZLE) return room.getPuzzle().isFinished();
		if (getType() == Door.Type.KEY && (Inventory.contains(getKeyId()) || !isLocked())) return true;
		if (getType() == Door.Type.GUARDIAN && !room.containsEnemies()) return true;
		if (getType() == Door.Type.SKILL) return Inventory.containsOneOf(getNeededSkillItem()) || getNeededSkillItem()[0] == 0;
		if (getType() == Door.Type.NORMAL) return true;
		return false;
	}
	
	/**
	 * Remove the skill object from the door
	 * @return returns true if the skill object is removed
	 */
	public boolean removeSkillObject(){
		getSkillObject().hover();
		getSkillObject().click();
		for (int i = 0; i < 100; i++){
			if (getSkillObject() == null) return true;
			Task.sleep(30);
			if (Main.getRunTime() - Main.getDungeon().msgLvlNeeded() < 1500){
				Main.getDungeon().addIgnoreDoor(this);
				return false;
			}
		}
		return getSkillObject() == null;
	}
	
	/**
	 * Open the door
	 * @return returns true if the door is open
	 */
	public boolean open(){
		if (door == null) return false;
		if (!room.getArea().contains(Players.getLocal().getLocation())) return false;
		if (Players.getLocal().getAnimation() > 0) return false;
		
		if (Calculations.distanceTo(door) > 5){
			getTileBeforeDoor().clickOnMap();
		}
		Camera.turnTo(door);
		for (int i = 0; i < 100; i++){
			if (Calculations.distanceTo(door) <= 5) break;
			Task.sleep(50);
		}
		
		if (getSkillObject() != null){
			if (!removeSkillObject()) return false;
		}
		
		if (isLocked()){
			getLock().hover();
			if (getLock().interact("Unlock")){
				for (int i = 0; i < 100; i++){
					if (!isLocked()){
						Main.getDungeon().removeKeyDoor(this);
						break;
					}
					if (Players.getLocal().isMoving()) i = 0;
					Task.sleep(30);
				}
			}
			if (isLocked()) return false;
		}
		door.hover();
		if (door.interact("Enter") || door.interact("Open")){
			for (int i = 0; i < 100 && Game.getClientState() == 11; i++){
				if (Main.getRunTime() - Main.getDungeon().msgLvlNeeded() < 1500){
					Main.getDungeon().addIgnoreDoor(this);
					return false;
				}
				if (i == 100) return false;
				Task.sleep(30);
			}
			
			for (int i = 0; i < 100; i++){
				if (Game.getClientState() == 11){
					//Main.getDungeon().addRoomRotation(Rooms.getRoom(getRoomTileBehindDoor()), getRotation().getRoomRotation());
					break;
				}
				Task.sleep(20);
			}
		}
		return isOpen();
	}
	
	/**
	 * Enter an already opened door
	 */
	public void enter(){
		if (room.inRoom()){
			if (SolvedPuzzle.solve(this)){
				if (Calculations.distanceTo(door) >= 5){
					getTileBeforeDoor().clickOnMap();
				}
				Camera.turnTo(door);
				for (int i = 0; i < 100; i++){
					if (Calculations.distanceTo(door) <= 5) break;
					Task.sleep(50);
				}
				if (Calculations.distanceTo(door) < 5 && door.isOnScreen()){
					door.hover();
					if (door.interact(Functions.inArray(Constants.KEY_DOORS, getObject().getId() - 145) ? "Open" : "Enter")){
						Task.sleep(1200);
						for (int i = 0; i < 100; i++){
							if (!room.inRoom()) break;
							if (!Players.getLocal().isMoving() && i < 70){
								i = 70;
							}
							Task.sleep(30);
						}
					}
				}
			}
		}
	}
}